// Package cos 为腾讯云对象存储接口
package cos

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/url"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"github.com/qor/oss"
	"github.com/qor/oss/cos-lib"
)

// Client Aliyun storage
type Client struct {
	Config *Config
	Bucket *cos.Bucket
}

// Config Aliyun client config
type Config struct {
	AppID     string
	SecretID  string
	SecretKey string
	Region    string
	Bucket    string
	Timetout  time.Duration
}

// New initialize COS storage
func New(config *Config) *Client {
	if config.Timetout == 0 {
		config.Timetout = time.Second * 10
	}
	client := &Client{Config: config}
	opt := &cos.Option{
		AppID:     config.AppID,
		SecretID:  config.SecretID,
		SecretKey: config.SecretKey,
		Region:    config.Region,
		Bucket:    config.Bucket,
	}
	cosClient := cos.New(opt)
	client.Bucket = cosClient.Bucket(config.Bucket)

	return client
}

// Get receive file with given path
func (client Client) Get(path string) (file *os.File, err error) {
	readCloser, err := client.GetStream(path)

	if err == nil {
		if file, err = ioutil.TempFile("/tmp", "ali"); err == nil {
			defer readCloser.Close()
			_, err = io.Copy(file, readCloser)
			file.Seek(0, 0)
		}
	}

	return file, err
}

// GetStream get file as stream
func (client Client) GetStream(path string) (io.ReadCloser, error) {
	ctx := cos.GetTimeoutCtx(client.Config.Timetout)
	buf := bytes.NewBuffer(make([]byte, 0, 4096))
	if err := client.Bucket.DownloadObject(ctx, client.ToRelativePath(path), buf); err != nil {
		return nil, err
	}
	return ioutil.NopCloser(buf), nil
}

// Put store a reader into given path
func (client Client) Put(urlPath string, reader io.Reader) (*oss.Object, error) {
	if seeker, ok := reader.(io.ReadSeeker); ok {
		seeker.Seek(0, 0)
	}
	ctx := cos.GetTimeoutCtx(client.Config.Timetout)
	err := client.Bucket.UploadObject(ctx, client.ToRelativePath(urlPath), reader, &cos.AccessControl{})
	now := time.Now()

	return &oss.Object{
		Path:             urlPath,
		Name:             filepath.Base(urlPath),
		LastModified:     &now,
		StorageInterface: client,
	}, err
}

// Delete delete file
func (client Client) Delete(path string) error {
	ctx := cos.GetTimeoutCtx(client.Config.Timetout)
	return client.Bucket.DeleteObject(ctx, client.ToRelativePath(path))
}

// List list all objects under current path
func (client Client) List(path string) ([]*oss.Object, error) {
	return nil, errors.New("不支持该操作")
}

// GetEndpoint get endpoint, FileSystem's endpoint is /
func (client Client) GetEndpoint() string {
	endpoint := fmt.Sprintf("%s-%s.cos.%s.myqcloud.com", client.Config.Bucket, client.Config.AppID, client.Config.Region)

	for _, prefix := range []string{"https://", "http://"} {
		endpoint = strings.TrimPrefix(endpoint, prefix)
	}

	return endpoint
}

var urlRegexp = regexp.MustCompile(`(https?:)?//((\w+).)+(\w+)/`)

// ToRelativePath process path to relative path
func (client Client) ToRelativePath(urlPath string) string {
	if urlRegexp.MatchString(urlPath) {
		if u, err := url.Parse(urlPath); err == nil {
			return strings.TrimPrefix(u.Path, "/")
		}
	}

	return strings.TrimPrefix(urlPath, "/")
}

// GetURL get public accessible URL
func (client Client) GetURL(path string) (url string, err error) {
	return path, nil
}
